#ifndef INSIM_H
#define INSIM_H

#include "insim_global.h"
#include <QtNetwork/QTcpSocket>
#include "insim_interface.h"

namespace LFS
{
    class INSIMSHARED_EXPORT InSim : public QObject, public InSimInterface
    {
        Q_OBJECT
        //! Define realized interface.
        Q_INTERFACES(LFS::InSimInterface);

    public:
        //! Default constructor.
        InSim();

        //! Default destructor.
        ~InSim();

        //! Setup data for connection.
        void setConnectionData(QString &newClientName, QString &newHostname,
                               quint16 newPort);

        //! Query connection data.
        void getConnectionData(QString &clientName, QString &hostname, quint16 &port);

        //! Connect to the specified host.
        void connectToHost(QString &newClientName, QString &newHostname, quint16 newPort);

        //! Connect to the stored host.
        void connectToHost();

        //! Disconnect the current connection.
        void disconnectFromHost();

        //! Return a pointer to the implementation.
        InSim *getInSim();

        //! Is connection up?
        bool isConnected();

    signals:
        //! Signal an error with this string.
        void insimErrorString(QString &errorString);
        //! Propagate socket connection to this class' client with this signal.
        void insimConnected();
        //! Propagate socket disconnection to this class' client with this signal.
        void insimDisconnected();

        /**************************************************
         * S O C K E T  S I G N A L L I N G  S L O T S
         */
    public slots:
        //! Called when there's data available.
        void readyRead();

        //! Called when TCP socket has set up the connection.
        void connected();

        //! Called when TCP socket has lost the connection.
        void disconnected();

        //! Called on errors from TCP socket.
        void error(QAbstractSocket::SocketError errorThatOccurred);

        //! Host was found.
        void hostFound();

#ifndef QT_NO_DEBUG_OUTPUT
        void stateChanged( QAbstractSocket::SocketState socketState )
        {
            debugSocketState(socketState);
        }
#endif

        //! Private methods.
    private:
        //! Send a tiny packet to LFS.
        qint64 sendTinyPacket(quint8 subType, quint8 requestInfo = 0);

        //! Template function for acquiring a certain type of message pointer.
        template<class T> T *getMessageStructure();

#ifndef QT_NO_DEBUG_OUTPUT
        //! Check socket state.
        void debugSocketState(QAbstractSocket::SocketState state)
        {
            QString debugString="InSim: ";
            switch(state)
            {
            case QAbstractSocket::UnconnectedState:
                debugString+="The socket is not connected.";
                break;
            case QAbstractSocket::HostLookupState:
                debugString+="The socket is performing a host name lookup.";
                break;
            case QAbstractSocket::ConnectingState:
                debugString+="The socket has started establishing a connection.";
                break;
            case QAbstractSocket::ConnectedState:
                debugString+="A connection is established.";
                break;
            case QAbstractSocket::BoundState:
                debugString+="The socket is bound to an address and port (for servers).";
                break;
            case QAbstractSocket::ClosingState:
                debugString+="The socket is about to close (data may still be waiting to be written).";
                break;
            case QAbstractSocket::ListeningState:
                debugString+="For internal use only.";
                break;
            default:
                debugString+="Unknown (impossible) state.";
                break;
            }
            qDebug(debugString.toAscii());
        }
#endif

        //! Data.
    private:
        //! Client application's name.
        QString insimClientName;

        //! Host IP address/name.
        QString insimHostname;

        //! Host port.
        quint16 insimPort;

        //! TCP socket for host connection.
        QTcpSocket insimSocket;

        //! Generic buffer for sent messages.
        QByteArray messageData;

        //! Generic buffer for read messages.
        QByteArray readMessage;
    };
}
#endif // INSIM_H
